【问题标题】:LINQ Distinct on multiple properties both directionsLINQ Distinct 在多个属性上双向
【发布时间】:2013-08-09 04:48:22
【问题描述】:

我正在尝试通过对象的两个属性过滤对象列表。如果有重复,则可以删除对象。如果第一个属性与第二个属性具有相同的值,并且第二个属性与第一个属性具有相同的值。

例子:

object0: id0=1A, id1=2B
object1: id0=1A, id1=2B
object2: id0=1A, id1=3C
object3: id0=2B, id1=1A
object4: id0=2B, id1=3C
object5: id0=3C, id1=2B

所以会发生以下情况: object0 删除 object1 和 object 3 object4 删除 object5

最终输出:

object0: id0=1A, id1=2B
object2: id0=1A, id1=3C
object4: id0=2B, id1=3C 

现在我有 FOR 循环来执行此操作,但我想知道是否有办法使用 linq 执行此操作?我尝试过使用 Distinct、DistinctBy 和 GroupBy。我需要自己比较才能完成吗?

【问题讨论】:

    标签: c# linq distinct


    【解决方案1】:

    这样做。

    source
      .GroupBy(x => new {min = Math.Min(x.Id0, x.Id1), max = Math.Max(x.Id0, x.Id1)})
      .Select(g => g.First());
    

    经过测试。

        public void SillyTuplesTest()
        {
            List<Tuple<string, int, int>> source = new List<Tuple<string, int, int>>()
            {
                Tuple.Create("object0", 1, 2),
                Tuple.Create("object1",1, 2),
                Tuple.Create("object2",1, 3),
                Tuple.Create("object3",2, 1),
                Tuple.Create("object4",2, 3),
                Tuple.Create("object5",3, 2)
            };
    
            var result = source
                .GroupBy(x => new { min = Math.Min(x.Item2, x.Item3), max = Math.Max(x.Item2, x.Item3) })
                .Select(g => g.First());
    
            foreach (Tuple<string, int, int> resultItem in result)
            {
                Console.WriteLine("{0} ({1}, {2})", resultItem.Item1, resultItem.Item2, resultItem.Item3);
            }
        }
    

    结果

    object0 (1, 2)
    object2 (1, 3)
    object4 (2, 3)
    

    对于字符串,您可以使用:

    source
      .GroupBy(x =>
        string.Compare(x.Id0, x.Id1, false) < 0 ?
        new {min = x.Id0, max = x.Id1} :
        new {min = x.Id1, max = x.Id0})
      .Select(g => g.First());
    

    如果您有未知数量的字符串,您可以使用 HashSet&lt;string&gt; 作为键和 SetComparer。

    IEqualityComparer<HashSet<string>> comparer = 
      HashSet<string>.CreateSetComparer();
    
    source
      .GroupBy(x => new HashSet<string>(x.GetStrings()), comparer)
      .Select(g => g.First());
    

    【讨论】:

    • 谢谢!它有效,但是如果 id 的值是字符串而不是整数,这仍然有效吗?我必须将字符串转换为数字吗?我编辑了我的问题以反映这一点。
    【解决方案2】:

    创建一个类似这样的扩展方法:

    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        var seenKeys = new HashSet<TKey>();
        return source.Where(element => seenKeys.Add(keySelector(element)));
    }
    

    那就用吧。

    看看这对你有什么作用。

    【讨论】:

    • 这不会产生所需的输出。事实上,考虑到 OP 的输入,它将产生每一个项目,而不是列出的三个。
    • OP 特别提到使用 DistinctBy,并且它对他不起作用(它不会,仅使用典型实现)。
    猜你喜欢
    • 2010-10-04
    • 1970-01-01
    • 2014-11-20
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多