Lambda表达式在使用All()时未返回正确的结果

本文关键字:返回 结果 表达式 All Lambda | 更新日期: 2023-09-27 17:57:35

我有这个类:

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

我有两个列表oldPricesnewPrices。两个列表都包含相同的项目,尽管实例不同。

newPrices中的一些商品有不同的价格,所以为了获得更改价格的列表,我正在做:

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

此表达式应该是正确的,但即使有更改,列表也是空的。

我错过了什么?

Lambda表达式在使用All()时未返回正确的结果

如果所有条件都为true,则

Enumerable.All返回true。这不是你想要检查的。你想得到与旧价格不同的所有新价格。

您必须先通过Id加入两个列表,才能比较价格:

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);

All检查oldPrices所有元素的条件是否为true。我想你想要的是检查具有相同ID的商品的价格是否发生了变化。

我建议首先将旧列表转换为字典:

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

然后像这样过滤新列表:

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

这将返回一个包含所有已更改(或新)项的词典。

您可以使Change类IEquatable<Change>允许一个简单的LINQ except()调用:

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;
    }
}
正如其他人所指出的,All要求序列的所有元素都满足一个条件。也可以使用Enumerable.Zip方法:
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);

您也可以尝试这两个左外部联接解决方案。。

  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);