【问题标题】:Partial Distinct Linq部分不同的 Linq
【发布时间】:2015-02-20 17:27:07
【问题描述】:

我有一个对象列表。这些对象有一个属性,例如“价值”。

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

我想说的是:

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

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

A
B
B

我已经找到了可以定义 Key-Selector 的 DistinctBy 解决方案。但结果当然是:

A
B

Cyral 的第一个答案完成了这项工作。所以我接受了。但 Scott 的回答确实是一个 PartialDistinct() 方法,看起来它解决了我所有的问题。

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

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

【问题讨论】:

  • TestNode 其他属性吗?剩下的单个对象的这些属性的值应该是多少?
  • 您是要在Value 上执行不同的操作,还是在TestNode where Value == A 加上正常的结果集 where Value != A 上执行不同的操作?

标签: c# linq


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    尝试找到与您的价值相匹配的元素,取出其中的第一个元素,然后将其与所有与您的价值不匹配的元素连接起来。

    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 不只过滤GetHashCodeEquals 中的ValueTake(1) 和 Distinct() 将有非常不同的行为。 (我没有 -1,我认为你的回答很好。如果他想要 Value 上的 Distinct,那么问题中的 OP 并不清楚,所以最好有两种方法)
    • @ScottChamberlain 哦,我明白你的意思了,我写错了,以为我在对值而不是对象调用 Distinct()。
    【解决方案3】:

    如果您想区分 TestNodeValue == A 加上正常结果集 Value != A 只需执行该确切过程并将其包装到扩展方法中。

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

    【讨论】:

    • 第二个版本可能是更好的解决方案
    • @KeithNicholas 你是对的,我更新了答案,只使用了第二个版本。
    • 第二种解决方案不起作用。 if(seen.Add(item)) 不会过滤掉带有“A”的对象。
    • 好吧,您要么需要传入一个检查Value 的比较器,要么覆盖GetHashCodeEquals 来比较属性Value 而不是默认比较。
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多