Partial Distinct Linq

非 Y 不嫁゛ 提交于 2021-01-27 15:59:53

问题


I have a list of objects. These objects have a property e.g. "Value".

       var lst = new List<TestNode>();

       var c1 = new TestNode()
        {
          Value = "A",
        };

        lst.Add(c1);

        var c2 = new TestNode()
        {
            Value = "A",
        };

        lst.Add(c2);

        var c3 = new TestNode()
        {
            Value = "B",
         };

        lst.Add(c3);

        var c4 = new TestNode()
        {
             Value = "B",
        };

        lst.Add(c4);

I would like to say something like:

lst.PartialDistinct(x => x.Value == "A")

This should only be distinct by predicate and when printing the "Value"s of the resulting IEnumerable the result should be:

A
B
B

I already found solutions for DistinctBy where it's possible to define a Key-Selector. But the result is then of course:

A
B

Cyral's first answer did the job. So I accepted it. But Scott's answer is really a PartialDistinct() Method as asked and looks like it solves all my problems.

Ok, thought it's solved with Scott's solution but it's not. Maybe I made a mistake while unit testing... or I don't know. The problem is:

if(seen.Add(item)) 

This does not filter out other objects with value "A".I think this is because it relies on referential equality when putting into hashset.

I ended up with following solution:

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)));
    }

回答1:


Try finding the elements that match your value, taking the first element of that, and then joining that with all that don't match your value.

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



回答2:


You can group by Value and perform 'conditional flattening' using SelectMany, i.e. take only one element from 'A' groups and all elements from the rest of the groups:

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



回答3:


If you are wanting to distinct on TestNode where Value == A plus the normal result set where Value != A just do that exact process and wrap it up in to a extension method.

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;
            }
        }
    } 



回答4:


Creating two different list and concatenating them is also a way.

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


来源:https://stackoverflow.com/questions/28618080/partial-distinct-linq

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!