如何在列表中删除重复的元素对

6
我是一名有用的助手,可以为您翻译文本。

我有一个包含整数对的列表。如果它们是重复的,该如何删除这些整数对?Distinct方法不起作用,因为可能存在(2, 1)和(1, 2)两种情况。

我的列表看起来像这样:

1, 2
2, 3
3, 1
3, 2
2, 4
4, 3

我不需要(2, 3)和(3, 2)。

我创建了一个公共结构FaceLine,其中包含public int AB,然后使用var faceline = new List<FaceLine>()进行赋值。

我是C#新手,很迷茫。


1
什么样的列表可以存储一对整数?是 List<Tuple<int,int>> 吗? - Tim Schmelter
1
你目前尝试了什么? - Yan
2
我创建了一个公共结构体 faceline,其中包含公共整型变量 a 和 b,然后声明了一个 List<faceline> 类型的 Faceline 列表。 - johnny_vonri
1
最好您编辑您的问题并在评论中添加代码。 - Alex K.
1
使用自定义类有三个选项。根据两个值覆盖EqualsGetHashCode(如果对该类有意义)。创建一个IEqualityComparer<T>,您可以将其传递给Distinct和其他方法和类。或者只需编写代码,以便在一对HashSet<Tuple<int,int>>中跟踪所见过的内容,这些内容都保持了顺序。 - juharr
显示剩余4条评论
4个回答

14
您可以使用自定义的IEqualityComparer<FaceLine>
public class UnorderedFacelineComparer : IEqualityComparer<FaceLine>
{
    public bool Equals(FaceLine x, FaceLine y)
    {
        int x1 = Math.Min(x.A, x.B);
        int x2 = Math.Max(x.A, x.B);
        int y1 = Math.Min(y.A, y.B);
        int y2 = Math.Max(y.A, y.B);
        return x1 == y1 && x2 == y2;
    }

    public int GetHashCode(FaceLine obj)
    {
        return obj.A ^ obj.B;
    }
}

那么查询非常简单:

var comparer = new UnorderedFacelineComparer();
List<FaceLine> nonDupList = faceLine
    .GroupBy(fl => fl, comparer)
    .Where(g => g.Count() == 1)
    .Select(g => g.First())
    .ToList();

如果你想保留其中一个重复项,只需要删除 Where 关键字:

List<FaceLine> nonDupList = faceLine
    .GroupBy(fl => fl, comparer)
    .Select(g => g.First())
    .ToList();

为什么要使用 g.First().Where(g => g.Count() == 1) 不已经包含了它吗? - styx
2
@styx:如果结果应该再次是一个List<FaceLine>,其中所有重复项都被删除,则不需要这样做。在.Where(g => g.Count() == 1).ToList()之后,我们得到一个List<System.Linq.IGrouping<FaceLine, FaceLine>>。查询不知道组仅包含一个元素,因此我们需要使用First - Tim Schmelter
你怎么知道“我们得到一个...xxx”是怎么回事,你怎么知道查询会在哪里解决?有什么神奇的东西可以帮助像我这样的人知道吗?那真的很有帮助。 - AussieJoe
1
@AussieJoe:至少在Visual Studio、Rider和MonoDevelop中,当你将鼠标悬停在一个方法上时,会显示该方法的返回类型。 - Rawcal
1
@AussieJoe:如果其他方法都失败了,就看看文档吧。例如https://msdn.microsoft.com/en-us/library/bb534334(v=vs.110).aspx告诉你`GroupBy`返回什么,然后对链中的每个方法重复此操作。过一段时间,你就会习惯它(如果你足够努力思考,它们都是有意义的)。 - Chris
@Rawcal 这已经是我现在的做法了,不知道还有没有更好的方法。谢谢。 - AussieJoe

3

使用 Linq:

List<List<int>> data = new List<List<int>>() {
    new List<int>() {1, 2},
    new List<int>() {2, 3},
    new List<int>() {3, 1},
    new List<int>() {3, 2},
    new List<int>() {2, 4},
    new List<int>() {4, 3}
};

List<List<int>> results = 
  data.Select(x => (x.First() < x.Last()) 
     ? new { first = x.First(), last = x.Last() } 
     : new { first = x.Last(), last = x.First() })
  .GroupBy(x => x)
  .Select(x => new List<int>() { x.First().first, x.First().last }).ToList();

3
如果您习惯使用常见的DistinctByLinq扩展(通过NuGet可用),您可以通过以下方式简单地完成此操作:
var result = list.DistinctBy(x => (x.A > x.B) ? (x.A, x.B) : (x.B, x.A));

示例控制台应用:

using System;
using System.Collections.Generic;
using MoreLinq;

namespace Demo
{
    class Test
    {
        public Test(int a, int b)
        {
            A = a;
            B = b;
        }

        public readonly int A;
        public readonly int B;

        public override string ToString()
        {
            return $"A={A}, B={B}";
        }
    }

    class Program
    {
        static void Main()
        {
            var list = new List<Test>
            {
                new Test(1, 2),
                new Test(2, 3),
                new Test(3, 1),
                new Test(3, 2),
                new Test(2, 4),
                new Test(4, 3)
            };

            var result = list.DistinctBy(x => (x.A > x.B) ? (x.A, x.B) : (x.B, x.A));

            foreach (var item in result)
                Console.WriteLine(item);
        }
    }
}

0
组成一组集合,您将免费获得该功能(每个较小的集合恰好包含两个整数)。

这是一个不错的方法,但是关于 .net 类(例如 HashSet<T>)的一些注释会很有用。 - spender
我觉得这个回答太过笼统,需要加入一些示例代码才能更好地解释。 - juharr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接