使用 LINQ 仅基于单个对象获取两个对象列表之间的差异 属性
Using LINQ to get the difference between two list of objects based only on a single object property
狗class有两个属性(名字和颜色)
假设我有两个 IEnumerable 列表:
List1 [{name="Sam", color="Fawn"}, {name="Mary", color=""}, {name="Bob", color=""}]
List2 [{name="Mary", color="Black"},{name="Bob", color="Yellow"}]
我想获取仅名称不同的狗对象列表
所以我的 return 列表看起来像
ListReturn: [{name="Sam", color="Fawn"}]
有意义吗?
我想用 linq 来做这件事。这是我试过的......但它不起作用
有帮助吗?
var missing = from l1 in List1
join l2 in List2 on l1.Name equals l2.Name into merged
from missed in merged.DefaultIfEmpty()
select missed;
我可能是个彻头彻尾的白痴,但我已经盯着这个看了一整天了,还是看不懂。任何帮助将不胜感激。
您可以分隔不需要的名称列表。在此之后,您可以使用 Any
和 !
(非)运算符来从该列表中过滤名称。例如:
var l2Names = List2.Select(x => x.Name);
var missing = from l1 in List1
where !l1.Any(x => l2Names.Contains(x.Name))
select l1;
您所拥有的在功能上是 Except
,但您不想使用整个项目的相等性,而是希望使用选定的 属性 作为键来执行 Except
。虽然您可以提供仅比较名称的自定义 IEqualityComparer
到 Except
,但编写该比较器是相当容易出错的样板代码。我们可以编写一个方法来相当容易地在投影键上执行 Except
:
public static IEnumerable<TSource> ExceptBy<TSource, TKey>(
this IEnumerable<TSource> source,
IEnumerable<TSource> other,
Func<TSource, TKey> keySelector)
{
var set = new HashSet<TKey>(other.Select(keySelector));
foreach(var item in source)
if(set.Add(keySelector(item)))
yield return item;
}
这会使用给定键执行 except,而不是自定义相等比较器。
现在您的查询很简单:
var query = list1.ExceptBy(list2, dog => dog.name);
最长的解法
private static void Main(string[] args)
{
var dogs1 = new List<Dog>
{
new Dog{Name = "Sam", Color = "Fawn"},
new Dog{Name = "Mary", Color = ""},
new Dog{Name = "Bob", Color = ""}
};
var dogs2 = new List<Dog>
{
new Dog{Name = "Mary", Color = "Black"},
new Dog{Name = "Bob", Color = "Yellow"}
};
var comparer = new Comparer();
var common = dogs1.Intersect(dogs2, comparer).ToList();
var res = dogs1.Except(common, comparer)
.Union(dogs2.Except(common, comparer));
}
public class Dog : INameable
{
public string Name { get; set; }
public string Color { get; set; }
}
public interface INameable
{
string Name { get; }
}
public class Comparer : IEqualityComparer<INameable>
{
public bool Equals(INameable x, INameable y)
{
return x.Name == y.Name;
}
public int GetHashCode(INameable obj)
{
return obj.Name.GetHashCode();
}
}
}
狗class有两个属性(名字和颜色)
假设我有两个 IEnumerable 列表:
List1 [{name="Sam", color="Fawn"}, {name="Mary", color=""}, {name="Bob", color=""}]
List2 [{name="Mary", color="Black"},{name="Bob", color="Yellow"}]
我想获取仅名称不同的狗对象列表
所以我的 return 列表看起来像
ListReturn: [{name="Sam", color="Fawn"}]
有意义吗?
我想用 linq 来做这件事。这是我试过的......但它不起作用 有帮助吗?
var missing = from l1 in List1
join l2 in List2 on l1.Name equals l2.Name into merged
from missed in merged.DefaultIfEmpty()
select missed;
我可能是个彻头彻尾的白痴,但我已经盯着这个看了一整天了,还是看不懂。任何帮助将不胜感激。
您可以分隔不需要的名称列表。在此之后,您可以使用 Any
和 !
(非)运算符来从该列表中过滤名称。例如:
var l2Names = List2.Select(x => x.Name);
var missing = from l1 in List1
where !l1.Any(x => l2Names.Contains(x.Name))
select l1;
您所拥有的在功能上是 Except
,但您不想使用整个项目的相等性,而是希望使用选定的 属性 作为键来执行 Except
。虽然您可以提供仅比较名称的自定义 IEqualityComparer
到 Except
,但编写该比较器是相当容易出错的样板代码。我们可以编写一个方法来相当容易地在投影键上执行 Except
:
public static IEnumerable<TSource> ExceptBy<TSource, TKey>(
this IEnumerable<TSource> source,
IEnumerable<TSource> other,
Func<TSource, TKey> keySelector)
{
var set = new HashSet<TKey>(other.Select(keySelector));
foreach(var item in source)
if(set.Add(keySelector(item)))
yield return item;
}
这会使用给定键执行 except,而不是自定义相等比较器。
现在您的查询很简单:
var query = list1.ExceptBy(list2, dog => dog.name);
最长的解法
private static void Main(string[] args)
{
var dogs1 = new List<Dog>
{
new Dog{Name = "Sam", Color = "Fawn"},
new Dog{Name = "Mary", Color = ""},
new Dog{Name = "Bob", Color = ""}
};
var dogs2 = new List<Dog>
{
new Dog{Name = "Mary", Color = "Black"},
new Dog{Name = "Bob", Color = "Yellow"}
};
var comparer = new Comparer();
var common = dogs1.Intersect(dogs2, comparer).ToList();
var res = dogs1.Except(common, comparer)
.Union(dogs2.Except(common, comparer));
}
public class Dog : INameable
{
public string Name { get; set; }
public string Color { get; set; }
}
public interface INameable
{
string Name { get; }
}
public class Comparer : IEqualityComparer<INameable>
{
public bool Equals(INameable x, INameable y)
{
return x.Name == y.Name;
}
public int GetHashCode(INameable obj)
{
return obj.Name.GetHashCode();
}
}
}