tags:

views:

72

answers:

4

Probably this question was already asked before, but my google-fu and SO-Search did not get me what what I was looking for.

I have a custom class, and a custom class comparer (for checking the equality of the class) implemented with IEqualityComparer.

public class Person
{
        public string Name { get; set; }
        public bool Flag { get; set; }
}

public class PersonComparer : IEqualityComparer<Person>
{
        #region IEqualityComparer<Person> Members

        public bool Equals(Person x, Person y)
        {
            //case insensitive compare
            return string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
        }

        public int GetHashCode(Person obj)
        {
            return base.GetHashCode();
        }

        #endregion
 }

and in the main portion of the code I have 2 lists "source" and "target"

Person bob = new Person() { Name = "Bob" };
Person sam = new Person() { Name = "Sam" };
Person andy = new Person() { Name = "Andy" };
Person thomas = new Person() { Name = "Thomas" };
Person jimmy = new Person() { Name = "Jimmy" };
Person sam2 = new Person() { Name = "sam" }; // note the lower case
Person jane = new Person() { Name = "Jane" };
List<Person> source = new List<Person>() { bob, sam, andy, thomas };
List<Person> target = new List<Person>() { sam2, andy,jane };

what I want to do

  1. update source list to only contain sam and andy, as bob and thomas are not in the target list. I did this

    source = (from p in source where (from t in target select t) .Contains(p, new PersonComparer()) select p).ToList();

  2. In the target I should "Flag" sam2 and andy to true and jane is flagged as "false" by default, I should not change it.

I tried using this, but this removes "jane" from target

//sets  sam2 & andy to true, removes Jane
target = (from p in target.Select(t => { t.Flag = true; return t; })
          where (from s in source
          select s).Intersect(select p).ToList();

Can any LINQ guru tell me what I am doing wrong ?

3.Is there a better way to write Query 1 ?

4.And finally a trivial question: how exactly do you say "=>" when you are talking to a fellow coder over the phone

+1  A: 

For part 4. => can be read as goes to.

RichardOD
Nice link. Thanks
ram
No worries. I think that's the question a lot of people wonder when starting out with LINQ.
RichardOD
+1  A: 

The GetHashCode() method should use the the obj passed instance, not its own parent.

recursive
Good point, more comment than answer to the question though. To be more precise: when .Equals(a, b) returns true, a.GetHashcode() should equal b.GetHashCode(). Also, when .Equals returns false, the hashcodes should differ
Sander Rijken
OK, so how would you write this GetHashCode method ?
ram
err, probably return obj.GetHashCode();
recursive
@Sander Rijken: I was with you until "Also, when `.Equals` returns `false`, the hashcodes should differ." It would be nice if this were possible, but in general it is not. Just think of `Int64.GetHashCode`. As `GetHashCode` is an `int` there are only 2^32 possible hash codes. But there are 2^64 different `long`s so at least two of them have the same hash code. In fact, at least 2^32 of them have the same hash code.
Jason
@Jason: nice catch, there can be collisions ofcourse. The code in the question can return a different hashcode for the an 'equal' object. I was trying to point out that that's buggy behavior.
Sander Rijken
+2  A: 

Linq isn't meant to update list, because it operates on IEnumerable<T>. You can create a new enumerable, based on source and target that represents the collection you need.

Something like this should work:

var combined = source.Where(x => target.Any(y => y == x))
Sander Rijken
+2  A: 

As Sander has pointed out, LINQ is for querying, not updating.

However, to answer the questions... Your original query of

source = (from p in source where (from t in target select t)
    .Contains(p, new PersonComparer()) select p).ToList();

would be much more simply written as:

source = source.Intersect(target, new PersonComparer()).ToList();

Having said that, you need to update PersonComparer as recursive mentioned. It should be something like this:

public int GetHashCode(Person obj)
{
    return obj == null ? 0 
         : StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name);
}

I'm afraid I don't really understand your second query particularly well... but if you want to change the existing objects, I'd suggest a foreach loop instead of trying to use LINQ. Queries with side-effects are generally a bad idea.

You may mean something like:

// You may want to make some singleton instance available, as this has no state
PersonComparer comparer = new PersonComparer();
foreach (Person person in target)
{
    if (source.Contains(person, comparer))
    {
        person.Flag = true;
    }
}
Jon Skeet
>>Queries with side-effects are generally a bad idea.Got it !! Thanks.
ram