1

我有一个电影列表,我需要将它们与另一个列表合并并复制。

我正在使用 Jon SkeetDistinctBy(m => m.SomeUniqueMovieProperty)来实现这一点,它工作正常。除了,我们很快发现会有 10-20% 的电影(在任一列表中)没有填写此属性,导致DistinctBy将它们折叠成 1 个幸运电影。

这是一个问题,我们希望保留所有没有此属性值的电影。最初我想从每个集合中提取这些电影,复制,然后再次合并它们,是否有更短的解决方案来解决这个问题?

4

5 回答 5

3

将结果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);
于 2016-06-20T19:57:06.273 回答
3

如果要包含所有空值,则需要将空属性替换为空值时唯一的值。假设属性是一个字符串,Guid 会很好地完成这项工作。

.DistinctBy(m => m.SomeUniqueMovieProperty ?? Guid.NewGuid().ToString())

任何时候它遇到具有空值的属性时,都会用一个随机的新 guid 值填充。


如果您还希望不删除空标题,请将查询更改为

.DistinctBy(m => String.IsNullOrEmpty(m.SomeUniqueMovieProperty) ? Guid.NewGuid().ToString() : m.SomeUniqueMovieProperty)

另一种选择是DistinctBy按照您想要的方式制作您自己的。这是原始源代码的调整版本,仅在shouldApplyFilter返回 true 时应用过滤器,为简洁起见,注释也被删除。

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));
于 2016-06-20T20:00:48.213 回答
2

也许您可以在复合不同键上过滤它们,如下所示

movies.DistinctBy(m => String.Format({0}{1}{...},m.prop1,m.prop2,[]));
于 2016-06-20T20:04:28.220 回答
2

假设m's Equals/GetHashCode没有被覆盖,如果m.SomeUniqueMoviePropertynull并且您没有任何其他唯一键,则可以将m其自身用作唯一键。

DistinctBy(m => (object) m.SomeUniqueMovieProperty ?? m)
于 2016-06-20T20:28:45.350 回答
1

最后一种方法,这可能是矫枉过正,您可以实现 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();
    }
}
于 2016-06-20T20:20:16.857 回答