我知道类似的问题已经通过了这个委员会,但我找不到一个回答我的问题。
我有2个list<myObject>
,有3个属性。
班级:
public int MeterID {get; set;}
public string Datum { get; set; }
public int NumberOfValues { get; set; }
public myObject(int meterID, string datum, int numberOfValues)
{
this.ID= ID;
this.Datum = datum;
this.NumberOfValues = numberOfValues;
}
我想根据以下2个值ID, Datum
比较这些列表,但我希望结果仍为List<MyObject>
因此,当我的对象在listA和listB中时,在执行以下linq语句之后它不应该出现在newList
中
var newList = ListA.Select(x => new { x.ID, x.Datum }).Except(ListB.Select(z => new { z.ID, z.Datum }));
该语句没有为我提供MyObject列表,但是列表中只包含ID和datum。我还需要ListA中的NumberOfValues属性在newList中。
有没有办法用linq实现这个目标?
答案 0 :(得分:6)
我会这样做:
var result = ListA.Where(a =>
ListB.All(b => b.ID != a.ID || b.Datum != a.Datum)).ToList();
这会占用ListA
的所有元素,其中 ListB
中的所有元素都不同(对于不同的定义)。
如果您还希望ListB
中的元素不在ListA
中,您可以将结果组合在一起:
var result = ListA.Where(a =>
ListB.All(b => b.ID != a.ID || b.Datum != a.Datum)).
Concat(ListB.Where(b =>
ListA.All(a => b.ID != a.ID || b.Datum != a.Datum)).
ToList();
答案 1 :(得分:2)
另一种选择是使用Enumerable.Except()和IEqualityComparer来排除listB
public class MyObject
{
public int MeterID { get; set; }
public string Datum { get; set; }
public int NumberOfValues { get; set; }
public MyObject(int meterID, string datum, int numberOfValues)
{
this.MeterID = meterID;
this.Datum = datum;
this.NumberOfValues = numberOfValues;
}
}
public class MyObjectEqualityComparer : EqualityComparer<MyObject>
{
public override bool Equals(MyObject x, MyObject y)
{
return x.MeterID == y.MeterID
&& x.Datum == y.Datum;
}
public override int GetHashCode(MyObject obj)
{
return obj.MeterID ^ obj.Datum.GetHashCode();
}
}
通过在外部对象中对比较的条件进行编码,可以更轻松地在其他地方重用它,并简化更改。
[TestClass]
public class CompareListsFixture
{
[TestMethod]
public void CompareLists()
{
var listA = new List<MyObject>
{
new MyObject(1,"A", 5), // Matches with 1/A/1
new MyObject(1,"B", 4),
new MyObject(2,"A", 3), // Matches with 2/A/4
new MyObject(2,"C", 2),
};
var listB = new List<MyObject>
{
new MyObject(1,"A", 1),
new MyObject(2,"A", 4),
new MyObject(3,"A", 8),
};
var expected = new List<MyObject>
{
new MyObject(1,"B", 4),
new MyObject(2,"C", 2),
};
var actual = listA.Except(listB, new MyObjectEqualityComparer());
CollectionAssert.AreEqual(expected, actual.ToList(), new MyObjectComparer());
}
}
我们使用比较器来比较Collection.Assert()
public class MyObjectComparer : Comparer<MyObject>
{
public override int Compare(MyObject x, MyObject y)
{
return Format(x).CompareTo(Format(y));
}
private static string Format(MyObject obj)
{
return $"{obj.MeterID}:{obj.Datum}:{obj.NumberOfValues}";
}
}