C# 部分相异Linq

C# 部分相异Linq,c#,linq,C#,Linq,我有一个物品清单。这些对象具有属性,例如“值” 这只能通过谓词区分,并且在打印结果IEnumerable的“值”时,结果应为: A B B 我已经找到了通过定义键选择器来区分的解决方案。但结果当然是: A B Cyral的第一个回答起了作用。所以我接受了。但是Scott的答案实际上是一个PartialDistinct()方法,看起来它解决了我所有的问题 好吧,我以为斯科特的解决方案解决了,但事实并非如此。也许我在单元测试时犯了一个错误。。。或者我不知道。问题是: if(seen.Add(it

我有一个物品清单。这些对象具有属性,例如“值”

这只能通过谓词区分,并且在打印结果IEnumerable的“值”时,结果应为:

A
B
B
我已经找到了通过定义键选择器来区分的解决方案。但结果当然是:

A
B
Cyral的第一个回答起了作用。所以我接受了。但是Scott的答案实际上是一个PartialDistinct()方法,看起来它解决了我所有的问题

好吧,我以为斯科特的解决方案解决了,但事实并非如此。也许我在单元测试时犯了一个错误。。。或者我不知道。问题是:

if(seen.Add(item)) 
这不会过滤掉值为“A”的其他对象。我认为这是因为它在放入hashset时依赖于引用等式

我最终得到了以下解决方案:

public static IEnumerable<T> PartialDistinct<T>(this IEnumerable<T> source Func<T, bool> predicate)
    {
        return source
            .Where(predicate)
            .Take(1)
            .Concat(source.Where(x => !predicate(x)));
    }
public static IEnumerable PartialDistinct(此IEnumerable源函数谓词)
{
返回源
.Where(谓词)
.采取(1)
.Concat(source.Where(x=>!谓词(x));
}

尝试查找与您的值匹配的元素,取其中的第一个元素,然后将其与所有与您的值不匹配的元素合并

var match = "A";
var result = lst
    .Where(x => x.Value == match)
    .Take(1)
    .Concat(lst
    .Where(x => x.Value != match))
    .Select(x => x.Value);

如果要在
TestNode
上进行区分,其中
Value==A
加上正常结果集,其中
Value!=一个
只需执行这个精确的过程,并将其封装到一个扩展方法中

public static class ExtensionMethods
{
    public static IEnumerable<T> PartialDistinct<T>(this IEnumerable<T> source, Func<T, bool> filter)
    {
        return PartialDistinct(source, filter, EqualityComparer<T>.Default);
    }

    public static IEnumerable<T> PartialDistinct<T>(this IEnumerable<T> source, Func<T, bool> filter, IEqualityComparer<T> comparer)
    {
        HashSet<T> seen = new HashSet<T>(comparer);
        foreach (var item in source)
        {
            if (filter(item))
            {
                if (seen.Add(item))
                {
                    yield return item;
                }
            }
            else
            {
                yield return item;
            }
        }
    } 
公共静态类扩展方法
{
公共静态IEnumerable PartialDistinct(此IEnumerable源,Func筛选器)
{
返回PartialDistinct(源、筛选器、EqualityComparer.Default);
}
公共静态IEnumerable PartialDistinct(此IEnumerable源、Func筛选器、IEqualityComparer比较器)
{
HashSet seen=新的HashSet(比较器);
foreach(源中的var项)
{
if(过滤器(项目))
{
如果(见添加(项目))
{
收益回报项目;
}
}
其他的
{
收益回报项目;
}
}
} 

创建两个不同的列表并连接它们也是一种方法

var answer = lst.Where(val=>val == "A").Select(note=>note.Value).Distinct().Concat(
        lst.Where(node => node.Value == "B").Select(node => node.Value));

您可以按
Value
分组,并使用
SelectMany
执行“条件展平”,即只从“A”组中选取一个元素,从其余组中选取所有元素:

var result = lst.GroupBy(x => x.Value)
                .SelectMany(g => g.Key == "A" ? g.Take(1) : g);

是否有
TestNode
其他属性?这些属性在单个剩余对象上的值应该是什么?是否要在
Value
上执行distinct,或在
TestNode
上执行distinct,其中
Value==a
加上正常结果集,其中
Value!=a
?取(1)和distinct()如果
TestNode
GetHashCode
中不只过滤
Value
Equals
,则会有非常不同的行为(我没有-1,我认为你的答案是好的。OP在问题中不清楚他是否想要一个不同的
值,所以这两种方法都很好)@ScottChamberlain噢,我明白你的意思了,我写这篇文章时错误地认为我是在对值而不是对象调用Distinct()。第二个版本可能更好solution@KeithNicholas你是对的,我更新了答案,只使用了第二个版本。第二个解决方案不起作用。如果(seen.Add(item))没有过滤出带有“A”的对象。好的,您需要传入一个比较器来检查
,或者重写
GetHashCode
等于
来比较属性
,而不是默认的比较。
var answer = lst.Where(val=>val == "A").Select(note=>note.Value).Distinct().Concat(
        lst.Where(node => node.Value == "B").Select(node => node.Value));
var result = lst.GroupBy(x => x.Value)
                .SelectMany(g => g.Key == "A" ? g.Take(1) : g);