0

I have the following (simplified) class:

public class CareRate {

  [Key]
  public int Id { get; set; }
  public string Description { get; set; }
  public decimal DayRate { get; set; }
}

I would like to compare two lists of CareRate only by their DayRate; one with CareRates containing the current DayRates and one with CareRates containing the DayRates to update. Other properties which might have changed like Description, should not be taken into account.

// Just a test method
public List<CareRate> FilterChangedCareRates(){
    var currentCareRates = new List<CareRate>{
        new CareRate { Id = 1, DayRate = 3,33, Description = "Some descr" }, 
        new CareRate { Id = 2, DayRate = 4,44, Description = "Some other descr" } 
    };

    var updatedCareRates = new List<CareRate>{
        new CareRate { Id = 1, DayRate = 2,22 }, 
        new CareRate {Id = 2, DayRate = 4,44 } // Unchanged
   };

    var actualUpdatedCareRates = new List<CareRate>();

    foreach(var updatedCareRate in updatedCareRates) {
        var currentCareRate = currentCareRates.Single(x => x.Id == updatedCareRate.Id); 
        if (updatedCareRate.DayRate != currentCareRate.DayRate) {
            actualUpdatedCareRates.Add(updatedCareRate); 
        }
    }
    return actualUpdatedCareRates;
}

The manner to filter the changed CareRate objects by Dayrate, feels a bit devious. I think I am overlooking something. What other and better options are there for acquiring the above?

Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
Melissa
  • 463
  • 5
  • 18
  • Im not entirely sure what you want to do with the lists you speak of.. are you just after a list of items where the rate changed, or an updated list of all the original items wth new rates? – BugFinder Nov 07 '17 at 13:44
  • Seems like a review. Should probably go to CodeReview.StachExchange.com – MakePeaceGreatAgain Nov 07 '17 at 13:45
  • @BugFinder would like to have a new list of CareRate that only have a changed DayRate (compared to the current DayRate). – Melissa Nov 07 '17 at 13:46
  • You can do this in one line: `return updatedCareRates.Where(updated => currentCareRates.Any(current => current.DayRate != updated.DayRate))`. – dbraillon Nov 07 '17 at 13:47
  • 1
    @HimBromBeere Codereview.stackexchange.com, yes. The non-simplified version would be preferred over there, please refer to the [help center](https://codereview.stackexchange.com/help/on-topic). – Mast Nov 07 '17 at 13:55
  • @S.Akbari I have posted an answer. I created a comparer to use in the `Except` extension method. – Melissa Nov 08 '17 at 12:01

4 Answers4

1

Simply use Zip method in LINQ:

var actualUpdatedCareRates = currentCareRates.Zip(updatedCareRates, 
                             (f, s) => f.DayRate != s.DayRate ? s : null)
                             .Where(c => c != null).ToList();
Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
0

I think, you can use something like this:

updatedCareRates.ForEach(x =>
{
    if (currentCareRates.FirstOrDefault(y => y.Id == x.Id && y.DayRate != x.DayRate) != null)
        actualUpdatedCareRates.Add(x);
});

Or in one line:

updatedCareRates.Where(x => currentCareRates.FirstOrDefault(y => y.Id == x.Id &&
                            y.DayRate != x.DayRate) != null).ToList()
                           .ForEach(x => actualUpdatedCareRates.Add(x));
Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
ValGiv
  • 1
  • 2
0

You can use this:

return (from up in updatedCareRates
            join cur in currentCareRates
            on up.Id equals cur.Id
            where up.DayRate != cur.DayRate
            select up).ToList();

Here is one of the rare cases where I think Query syntax is better than Method syntax.

PinBack
  • 2,499
  • 12
  • 16
0

Instead of using an Where(x => x...), I looked for a solution with using the Except method as posted here.

I created a class DayRateComparer as showed below.

public class DayRateComparer : IEqualityComparer<CareRate>
{
    public bool Equals(CareRate x, CareRate y) {
        if (x = null) throw new ArgumentNullException(nameof(x));
        if (y = null) throw new ArgumentNullException(nameof(y));

        return x.Id == y.Id && x.DayRate == y.DayRate;
    }

    public int GetHashCode(CareRate obj) {
        retun obj.Id.GetHashCode() + obj.DayRate.GetHashCode(); 
    }
}

And I used the DayRateComparer like this:

// Just a test method
public List<CareRate> FilterChangedCareRates(){
    var currentCareRates = new List<CareRate>{
        new CareRate { Id = 1, DayRate = 3,33, Description = "Some descr" }, 
        new CareRate { Id = 2, DayRate = 4,44, Description = "Some other descr" } 
    };

    var updatedCareRates = new List<CareRate>{
        new CareRate { Id = 1, DayRate = 2,22 }, 
        new CareRate {Id = 2, DayRate = 4,44 } // Unchanged
   };

    return updatedCareRates.Except(currentCareRates, new DayRateComparer()).ToList();
}

I don't like the use of a temporary list (like actualUpdatedCareRates) and this is not necessary anymore when using a comparer. The Zip method as mentioned by @S.Akbari is also a clean and short way but looks a bit complex for me at first sight. Thanks all for your posts.

Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
Melissa
  • 463
  • 5
  • 18
  • No `Zip` is not complex as you think. Just go and read how does it works in the link that I've mentioned in my answer. It makes your works a lot easier than writing a comparer for this purpose while you can easily use `Zip` method IMO. – Salah Akbari Nov 08 '17 at 12:04
  • @S.Akbari, you are right. It isn't that complex and it does what it should do. I marked your answer as the solution. – Melissa Nov 10 '17 at 11:51