1

I have this class:

public class Change()
{
    public int Id {get; set;}
    public decimal Price {get; set;}
}

And I have 2 lists oldPrices and newPrices. Both lists contain the same items albeit different instances.

Some items in newPrices have different prices, so to get a list of changed prices I'm doing:

var list = newPrices
             .Where(x => oldPrices.All(p => p.Price != x.Price))
             .ToDictionary(x => x.Id, x => x.Price);

This expression should be correct but the list is empty even though there are changes.

What am I missing?

3
  • 6
    All requires that all prices be different. Did you mean to use Any? Commented Jul 19, 2016 at 13:28
  • Are you looking for items with the same Id but different prices? Or any items with a different price regardless of the id? Commented Jul 19, 2016 at 13:29
  • just read through the LINQ query; it's specifically designed to read well in English, "yield all of the items in NewPrices where the new price is different from all of the prices in 'oldPrices'" Or, worded another way, "give me all of the items where the current price doesn't exist in 'oldPrices'." That's not what you said you wanted. Commented Jul 19, 2016 at 13:30

5 Answers 5

2

Enumerable.All returns true if all conditions are true. That's not what you want to check. You want to get all new-prices which differ from the old-prices.

You have to join both lists by Id first to be able to compare the prices:

var joined = from np in newPrices
             join op in oldPrices
             on np.Id equals op.Id
             where np.Price != op.Price
             select np;
var newPricesById = joined.ToDictionary(p => p.Id, p => p.Price);
Sign up to request clarification or add additional context in comments.

Comments

1

All checks that the condition is true for all elements in oldPrices. I guess what you want is to check if the price of the item with the same ID has changed.

I suggest to first convert the old list into a dictionary:

var oldPricesDic = oldPrices.ToDictionary(x => x.Id, x => x.Price);

And then filter the new list like this:

var list = newPrices
             .Where(x => !oldPricesDic.ContainsKey(x.Id) ||
                         oldPricesDic[x.Id] != x.Price)
             .ToDictionary(x => x.Id, x => x.Price);

This now returns a dictionary with all changed (or new) items.

Comments

1

You could make the Change class IEquatable<Change> allowing a simple LINQ except() call:

void Main()
{
    var oldPrices = new List<Change> {
        new Change { Id = 1, Price = 1.5M },
        new Change { Id = 2, Price = 5.0M }
    };

    var newPrices = new List<Change> {
        new Change { Id = 1, Price = 1.5M },
        new Change { Id = 2, Price = 5.75M }
    };

    var list = newPrices.Except(oldPrices).ToDictionary(x => x.Id, x => x.Price);
}

public class Change : IEquatable<Change>
{
    public int Id {get; set;}
    public decimal Price {get; set;}

    public bool Equals(Change other)
    {
        if (Object.ReferenceEquals(other, null)) return false;
        if (Object.ReferenceEquals(this, other)) return true;
        return Id.Equals(other.Id) && Price.Equals(other.Price);
    }

    public override int GetHashCode()
    {
        int hashId = Id.GetHashCode();
        int hashPrice = Price.GetHashCode();
        return hashId ^ hashPrice;
    }
}

Comments

0

As others pointed All requires that all elements of a sequence satisfy a condition. It can also be done by using Enumerable.Zip method:

var dictionary = newPrices.OrderBy(c => c.Id)
                .Zip(oldPrices.OrderBy(p => p.Id), (c, p) => p.Price != c.Price ? c : null)
                .Where(c => c != null).ToDictionary(c => c.Id, c => c.Price);

Comments

0

you may try also this 2 left outer join solutions..

  var pricesFromLeftJoin =
                    (from np in newPrices
                     join op in oldPrices on np.Id equals op.Id into subOldPrices
                     from subPrice in subOldPrice.DefaultIfEmpty()
                     where subPrice == null || (subPrice != null && subPrice .Price != np.Price)
                     select np).ToDictionary(x => x.Id, x => x.Price);


  var pricesFromLeftJoin_compactForm =
                (from np in newPrices
                 from op in oldPrices.Where(op => (op.Id == np.Id)).DefaultIfEmpty() 
                 where op == null || (op != null && op.Price != np.Price)
                 select np).ToDictionary(x => x.Id, x => x.Price);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.