1

I have Cell objects which contain a List<int> called PossibleValues. I'm trying to find a way to get a list of cells in which all members have matching PossibleValues. I currently have:

foreach (var cell in group)
{
    var cellsWithMatchingPossibleValues = group.Where(c => c.PossibleValues == cell.PossibleValues);
}

Unfortunately this isn't working, I suspect my linq statement isn't comparing the contents of PossibleValues, but instead comparing a reference of some kind, so that even in the case where both lists are composed of 3 and nothing else, cellsWithMatchingPossibleValues ends up only containing one cell, although I'm not certain, or sure how to get around that.

To formalise the question: How can I return objects which contain Lists based on the those lists matching?

MGDavies
  • 1,024
  • 1
  • 17
  • 30
  • 1
    group in foreach and group inside foreach is different, isn't it? – Valentin Mar 13 '16 at 14:17
  • 1
    .ToList(); add this at the end of LINQ statement – CodeConstruct Mar 13 '16 at 14:17
  • Valentin: Same group, I'm looking to find how many Cells within that group have matching PossibleValues lists, to do that I planned to loop through each cell and match that list against the lists of the whole group. I'd expect to always get 1 match with this, seeing as the cell is in the group. If you have a more efficient idea, I'd love to hear it. – MGDavies Mar 13 '16 at 14:20
  • CodeConstruct: The issue isn't solved by doing a .ToList(); The resulting list still only contains one object when we would expect two. – MGDavies Mar 13 '16 at 14:22
  • 1
    Please let me know if I'm missing something, but wouldn't the GroupBy function be more what you're looking for here? Also, doesn't this code re-initialize cellsWithMatchingPossibleValues with every iteration, basically removing whatever value was there before? – Dan Orlovsky Mar 13 '16 at 14:37
  • 2
    What is `PossibleValues`? Is it a list of strings, ints, or objects? Your answer to this question will make a **huge** difference in whether the answer is facile or complex. – code4life Mar 13 '16 at 14:53
  • 1
    Second question: are you iterating a collection of `Cell` objects and distilling a list of `Cell` objects having a common set of `PossibleValue`s, or do you have a single `Cell` object and trying to see which `Cell`s in a list match to it? – code4life Mar 13 '16 at 14:57
  • 1
    Can you clarify what you mean by "matching"? Same elements? Same elements in the same order? – Bobson Mar 13 '16 at 15:16
  • Valid questions: PossibleValues is a list of ints. Group is a List. Each Cell has a PossibleValues List. I'll be updating the main question to improve it in light of some of the questions I've had. – MGDavies Mar 13 '16 at 15:19
  • Bobson: Matching just means same elements. – MGDavies Mar 13 '16 at 15:51

2 Answers2

2

You can implement IEqualityComparer interface and use GroupBy method. Here you can find good GetHashCode for List, and here how to compare a lists.

    public class PossibleValuesCellComparer : IEqualityComparer<Cell>
    {
        public bool Equals(Cell x, Cell y)
        {
            return Enumerable.SequenceEqual(x.PossibleValues.OrderBy(t => t), y.PossibleValues.OrderBy(t => t));
        }


        public int GetHashCode(Cell cell)
        {
            var list = cell.PossibleValues.OrderBy(t => t);
            unchecked
            {
                int hash = 19;
                foreach (var obj in list)
                {
                    hash = hash * 31 + obj.GetHashCode();
                }
                return hash;
            }
        }
    }

     ....

     var g2 = group.GroupBy(x => x, new PossibleValuesCellComparer());
Community
  • 1
  • 1
Valentin
  • 5,380
  • 2
  • 24
  • 38
2

You can implement your own IEqualityComparer<Cell> for your Cell class that states equality when the PossibleValues are equal like this:

public class CellComparer : IEqualityComparer<Cell>
{
    public bool Equals(Cell x, Cell y)
    {
        if (ReferenceEquals(x, null)) return ReferenceEquals(y, null);
        if (ReferenceEquals(y, null)) return false;
        return x.PossibleValues.SequenceEqual(y.PossibleValues);
    }
    public int GetHashCode(Cell obj)
    {
        if (obj == null) return 0;
        unchecked
        {
            int hash = 1;
            foreach (int h in obj.PossibleValues.Select(v => v?.GetHashCode() ?? 0))
                hash = (hash * 397) ^ h;
            return hash;
        }
    }
}

Then you can use this for a simple LINQ grouping like this:

var cellsGroupedByEqualValues = group.GroupBy(c => c, new CellComparer());

This returns an IEnumerable<IGrouping<Cell,Cell>> and you can iterate through it and receive the number of matching cells:

foreach(var groupedCells in cellsGroupedByEqualValues)
    Console.WriteLine(groupedCells.Count());

But these contain duplicates since GroupBy generates a IGrouping for every Cell and adds all matching cells to that. (still trying to find a good way around that)

But for now you can tell for every Cell how many other Cells with the same list of values there are.

René Vogt
  • 43,056
  • 14
  • 77
  • 99