3

I'm writing a simple search page for our intranet application. When a user searches for n words I create n lists of hits and then want to return only the results that are common to all lists.

I have something working using List<int> thus:

var list1 = new List<int> {1,2,3,4,5,8,10};
var list2 = new List<int> {3,5,6,9,10,11};
var list3 = new List<int> {3,4,5,10};

var listOfLists = new List<List<int>>{list1,list2,list3};

var results = listOfLists.Aggregate((prevList, nextList) => prevList
                 .Intersect(nextList)
                 .ToList()); 

results.Dump(); //correctly returns 3,5,10, which are common to all lists

However if I try this with my SearchResults class I get no results. Here's the code:

//results from first search word "howard"
List<SearchResult> list1 = new List<SearchResult>();
list1.Add(new SearchResult ("a.html","howard kent"));
list1.Add(new SearchResult ("b.html","howard shaw")); //the common item
list1.Add(new SearchResult ("c.html","howard smith"));
list1.Add(new SearchResult ("d.html","howard moore"));

//results from second search word "shaw"
List<SearchResult> list2 = new List<SearchResult>();
list2.Add(new SearchResult ("e.html","jon shaw"));
list2.Add(new SearchResult ("b.html","howard shaw")); //the common item
list2.Add(new SearchResult ("f.html","chris shaw"));

//could be n further lists...

//make a list of lists
List<List<SearchResult>> searchLists = new List<List<SearchResult>>();
searchLists.Add(list1);
searchLists.Add(list2);

//find results that are common to all lists.
//Doesn't work - returns nil items, should return 1
var results = searchLists
             .Aggregate((prevList, nextList) => prevList
             .Intersect(nextList)
             .ToList()); 

results.Dump();

}

class SearchResult
{
    public string Url{get;set;}
    public string SearchText { get; set; }

//constructor
public SearchResult(string url,string searchText)
{
    Url = url;
 SearchText = searchText;
}

How should I change the query to return the result I want?

Howard Shaw
  • 1,061
  • 1
  • 10
  • 18

2 Answers2

3

That's because the SearchResult objects are not the same objects, although they have the same data. You can fix this by implementing the IEquatable<T> interface. http://msdn.microsoft.com/en-us/library/ms131187(v=vs.110).aspx

Ie.

public class SearchResult : IEquatable<SearchResult>
{
    public string Url{get;set;}
    public string SearchText { get; set; }
    public bool Equals(SearchResult other)
    {
        if (other == null)
            return false;
        return string.Concat(this.Url, this.SearchText).Equals(string.Concat(other.Url, other.SearchText), StringComparison.OrdinalIgnoreCase);
    }
}

Another solution would be to implement a IEqualityComparer<T> and pass it on to the Enumerable.Intersect method.

Torbjörn Hansson
  • 18,354
  • 5
  • 33
  • 42
1

You can use Enumberable.Intersect to get the common items. But in your case you are comparing two object. So you need to implement the IEqualityComparer Interface and create your own comparer class.

I am assuming your SearchResult class like this

public class SearchResult
    {
        public SearchResult(string first, string second)
        {
            First = first;
            Second = second;
        }

        public string First { get; set; }
        public string Second { get; set; }

    }

Now the IEqualityComparer interface implementation.

 public class MyCompare : IEqualityComparer<SearchResult>
    {
        public bool Equals(SearchResult x, SearchResult y)
        {
            return (x.First == y.First) && ((x.Second == y.Second));
        }


        public int GetHashCode(SearchResult obj)
        {
            unchecked
            {
                int hash = 17;
                hash = hash * 23 + obj.First.GetHashCode();
                hash = hash * 23 + obj.Second.GetHashCode();
                return hash;
            }
        }
}

The important point is within the GetHashCode where we need to incorporate both the properties instead of one. If we were comparing single property then it would be simple like this

return obj.A.GetHashCode();

But here we are comparing multiple properties.So that we need to change its implementation. Thanks to @Tim to their post

Now you can use the Intersect method to get the common items like this

var common = list1.Intersect<SearchResult>(list2,new MyCompare()).ToList();
Community
  • 1
  • 1
Sachin
  • 40,216
  • 7
  • 90
  • 102
  • Thanks Sachin, isn't the last query just comparing two lists - I don't know how many list I might have - it will depend on how many search terms the user enters. – Howard Shaw Feb 15 '14 at 13:41
  • Yes @HowardShaw it will depend on your implementation that when should two object called as same (means which are the properties should be equal to be a common object.) – Sachin Feb 15 '14 at 13:45