DistinctBy 但忽略 null/empty
DistinctBy but ignore null/empty
我有一个电影列表,我需要将它们与另一个列表合并并复制。
我正在使用 Jon Skeet 的 DistinctBy(m => m.SomeUniqueMovieProperty)
来实现这个并且它工作正常。除此之外,我们很快发现,在某些情况下,有 10-20% 的电影(在任一列表中)没有填写此 属性,导致 DistinctBy
将它们折叠成 1 部幸运电影。
这是个问题,我们想保留所有没有价值的电影 属性。最初我想从每个集合中提取这些电影,复制,然后再次合并它们,这个问题有没有更短的解决方案?
将 DistinctBy()
的结果与 Where([null or empty]).
的结果连接起来
var nullMovies = allMovies.Where(m=>string.IsNullOrEmpty(m.SomeUniqueMovieProperty));
var distinctNonNullMovies = allMovies.Where(m => !string.IsNullOrEmpty(m.SomeUniqueMovieProperty)).DistinctBy(m => m.SomeUniqueMovieProperty);
var result = nullMovies.Concat(distinctNonNullMovies);
如果要包含所有空值,则需要将空值 属性 替换为空值时唯一的内容。假设 属性 是一个字符串,Guid 可以很好地完成这项工作。
.DistinctBy(m => m.SomeUniqueMovieProperty ?? Guid.NewGuid().ToString())
任何时候它遇到一个 属性 空值时,它都会被随机填充一个新的 guid 值。
如果您还希望不删除空标题,请将查询更改为
.DistinctBy(m => String.IsNullOrEmpty(m.SomeUniqueMovieProperty) ? Guid.NewGuid().ToString() : m.SomeUniqueMovieProperty)
另一种选择是制作您自己的 DistinctBy
以您想要的方式运行。这是 original source 的调整版本,仅在 shouldApplyFilter
returns 为真时才应用过滤器,为简洁起见,注释也被删除。
static partial class MoreEnumerable
{
public static IEnumerable<TSource> ConditionalDistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, Func<TKey, bool> shouldApplyFilter)
{
return source.ConditionalDistinctBy(keySelector, shouldApplyFilter, null);
}
public static IEnumerable<TSource> ConditionalDistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, Func<TKey, bool> shouldApplyFilter, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (keySelector == null) throw new ArgumentNullException("keySelector");
if (shouldApplyFilter == null) throw new ArgumentNullException("shouldApplyFilter");
return ConditionalDistinctByImpl(source, keySelector, shouldApplyFilter, comparer);
}
private static IEnumerable<TSource> ConditionalDistinctByImpl<TSource, TKey>(IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, Func<TKey, bool> shouldApplyFilter, IEqualityComparer<TKey> comparer)
{
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
var key = keySelector(element);
if (shouldApplyFilter(key) && knownKeys.Add(key))
{
yield return element;
}
}
}
}
它会像
一样使用
.ConditionalDistinctBy(m => m.SomeUniqueMovieProperty, s => !String.IsNullOrEmpty(s));
也许您可以在复合不同键上过滤它们,如下所示
movies.DistinctBy(m => String.Format({0}{1}{...},m.prop1,m.prop2,[]));
最后一种方法可能有点矫枉过正,您可以实现 IEqualityComparer 并在 null 被认为是唯一的情况下将逻辑放在那里。 DistinctBy 对这种情况有重载。
public class MovieComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (x == null || y == null)
{
return false;
}
return x == y;
}
public int GetHashCode(string obj)
{
if (obj == null)
{
return 0;
}
return obj.GetHashCode();
}
}
假设 m
的 Equals
/GetHashCode
没有被覆盖,如果 m.SomeUniqueMovieProperty
是 null
并且您没有任何其他唯一键,您可以使用 m
本身作为唯一键。
DistinctBy(m => (object) m.SomeUniqueMovieProperty ?? m)
我有一个电影列表,我需要将它们与另一个列表合并并复制。
我正在使用 Jon Skeet 的 DistinctBy(m => m.SomeUniqueMovieProperty)
来实现这个并且它工作正常。除此之外,我们很快发现,在某些情况下,有 10-20% 的电影(在任一列表中)没有填写此 属性,导致 DistinctBy
将它们折叠成 1 部幸运电影。
这是个问题,我们想保留所有没有价值的电影 属性。最初我想从每个集合中提取这些电影,复制,然后再次合并它们,这个问题有没有更短的解决方案?
将 DistinctBy()
的结果与 Where([null or empty]).
var nullMovies = allMovies.Where(m=>string.IsNullOrEmpty(m.SomeUniqueMovieProperty));
var distinctNonNullMovies = allMovies.Where(m => !string.IsNullOrEmpty(m.SomeUniqueMovieProperty)).DistinctBy(m => m.SomeUniqueMovieProperty);
var result = nullMovies.Concat(distinctNonNullMovies);
如果要包含所有空值,则需要将空值 属性 替换为空值时唯一的内容。假设 属性 是一个字符串,Guid 可以很好地完成这项工作。
.DistinctBy(m => m.SomeUniqueMovieProperty ?? Guid.NewGuid().ToString())
任何时候它遇到一个 属性 空值时,它都会被随机填充一个新的 guid 值。
如果您还希望不删除空标题,请将查询更改为
.DistinctBy(m => String.IsNullOrEmpty(m.SomeUniqueMovieProperty) ? Guid.NewGuid().ToString() : m.SomeUniqueMovieProperty)
另一种选择是制作您自己的 DistinctBy
以您想要的方式运行。这是 original source 的调整版本,仅在 shouldApplyFilter
returns 为真时才应用过滤器,为简洁起见,注释也被删除。
static partial class MoreEnumerable
{
public static IEnumerable<TSource> ConditionalDistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, Func<TKey, bool> shouldApplyFilter)
{
return source.ConditionalDistinctBy(keySelector, shouldApplyFilter, null);
}
public static IEnumerable<TSource> ConditionalDistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, Func<TKey, bool> shouldApplyFilter, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (keySelector == null) throw new ArgumentNullException("keySelector");
if (shouldApplyFilter == null) throw new ArgumentNullException("shouldApplyFilter");
return ConditionalDistinctByImpl(source, keySelector, shouldApplyFilter, comparer);
}
private static IEnumerable<TSource> ConditionalDistinctByImpl<TSource, TKey>(IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, Func<TKey, bool> shouldApplyFilter, IEqualityComparer<TKey> comparer)
{
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
var key = keySelector(element);
if (shouldApplyFilter(key) && knownKeys.Add(key))
{
yield return element;
}
}
}
}
它会像
一样使用.ConditionalDistinctBy(m => m.SomeUniqueMovieProperty, s => !String.IsNullOrEmpty(s));
也许您可以在复合不同键上过滤它们,如下所示
movies.DistinctBy(m => String.Format({0}{1}{...},m.prop1,m.prop2,[]));
最后一种方法可能有点矫枉过正,您可以实现 IEqualityComparer 并在 null 被认为是唯一的情况下将逻辑放在那里。 DistinctBy 对这种情况有重载。
public class MovieComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (x == null || y == null)
{
return false;
}
return x == y;
}
public int GetHashCode(string obj)
{
if (obj == null)
{
return 0;
}
return obj.GetHashCode();
}
}
假设 m
的 Equals
/GetHashCode
没有被覆盖,如果 m.SomeUniqueMovieProperty
是 null
并且您没有任何其他唯一键,您可以使用 m
本身作为唯一键。
DistinctBy(m => (object) m.SomeUniqueMovieProperty ?? m)