如何使用LINQ从List<string>中删除重复组合

3

我有一个字符串列表,如下所示:

List<string> MyList = new List<string>
{ 
    "A-B", 
    "B-A", 
    "C-D", 
    "C-E", 
    "D-C",
    "D-E",
    "E-C",
    "E-D",
    "F-G",
    "G-F"
};

我需要从列表中删除重复的内容,即如果存在"A-B"和"B-A",则只保留"A-B"(第一个条目)。

因此,结果将会是这样的:

"A-B"   
"C-D"
"C-E"   
"D-E"
"F-G"

有没有使用LINQ的方法来实现这个?

每个字符串都一定是三个字符吗? - Mark Byers
@ Mark Byres:不,它可以超过三个字符。 - Thorin Oakenshield
@Pramodh:列表初始时是否总是已排序? - Mark Byers
@ Mark Bayers:永远不会,它不会按顺序。 - Thorin Oakenshield
@ Fredrik Mörk:不需要按顺序。 - Thorin Oakenshield
显示剩余2条评论
6个回答

14

实现一个 IEqualityComparer ,使得 Equals("A-B", "B-A") 返回 true,并使用 Enumerable.Distinct 方法。


1
实现IEqualityComparer的示例将为您带来答案的认可 ;) - abatishchev
4
我不知道 - 提供一个IEqualityComparer的实现感觉像是在为别人做作业... - Niki

12

这会返回您要查找的序列:

var result = MyList
    .Select(s => s.Split('-').OrderBy(s1 => s1))
    .Select(a => string.Join("-", a.ToArray()))
    .Distinct();

foreach (var str in result)
{
    Console.WriteLine(str);
}

简而言之:将每个字符串根据 - 字符拆分成包含两个元素的数组。对每个数组进行排序,然后将它们拼接在一起。然后您可以简单地使用 Distinct 获取唯一值。

更新:经过更深思考,我意识到您可以轻松地删除其中一个 Select 调用:

var result = MyList
    .Select(s => string.Join("-", s.Split('-').OrderBy(s1 => s1).ToArray()))
    .Distinct();

免责声明:无论原始序列中它们出现的顺序如何,此解决方案将始终保留值“A-B”而不是“B-A”。


4
给投票反对者,请留下评论,以便纠正任何错误。 - Fredrik Mörk
+1,虽然有一点小问题。Distinct方法被定义为返回一个无序集合,所以要完全正确,您需要对结果进行排序以获得OP指定的顺序。但是它实现为有序集合,所以最多只是一个小问题。 - JaredPar

4

这假设 - 前后的位总是单个字符(或者你想要回文相等)。提问者指出这个假设不成立。如果你修正回答,我会点赞。(顺便一提,在教育的利益下,请给它一个更具描述性的名称,而不仅仅是“Comparer”。) - Timwi

1

非常基础,但可以写得更好(但它只是工作):

class Comparer : IEqualityComparer<string>
  {
      public bool Equals(string x, string y)
      {
          return (x[0] == y[0] && x[2] == y[2]) || (x[0] == y[2] && x[2] == y[0]);
      }

      public int GetHashCode(string obj)
      {
          return 0;
      }
  }

var MyList = new List<String>
{ 
    "A-B", 
    "B-A", 
    "C-D", 
    "C-E", 
    "D-C",
    "D-E",
    "E-C",
    "E-D",
    "F-G",
    "G-F"
}
.Distinct(new Comparer());

foreach (var s in MyList)
{
    Console.WriteLine(s);
}

2
有一个微妙的错误:如果你运行调试器,你应该注意到使用你的代码 E-C 等于 E-D,这是不正确的... - code4life

1
你需要像这样实现IEqualityComparer:
public class CharComparer : IEqualityComparer<string>
{
    #region IEqualityComparer<string> Members

    public bool Equals(string x, string y)
    {
        if (x == y)
            return true;

        if (x.Length == 3 && y.Length == 3)
        {
            if (x[2] == y[0] && x[0] == y[2])
                return true;

            if (x[0] == y[2] && x[2] == y[0])
                return true;
        }

        return false;
    }

    public int GetHashCode(string obj)
    {
        // return 0 to force the Equals to fire (otherwise it won't...!)
        return 0;
    }

    #endregion
}

示例程序:

class Program
{
    static void Main(string[] args)
    {
        List<string> MyList = new List<string>
        { 
            "A-B", 
            "B-A", 
            "C-D", 
            "C-E", 
            "D-C",
            "D-E",
            "E-C",
            "E-D",
            "F-G",
            "G-F"
        };

        var distinct = MyList.Distinct(new CharComparer());
        foreach (string s in distinct)
            Console.WriteLine(s);

        Console.ReadLine();
    }
}

结果:
"A-B"
"C-D"
"C-E"
"D-E"
"F-G"

-2
int checkID = 0;
while (checkID < MyList.Count)
{
 string szCheckItem = MyList[checkID];
 string []Pairs = szCheckItem.Split("-".ToCharArray());
 string szInvertItem = Pairs[1] + "-" + Pairs[0];
 int i=checkID+1;
 while (i < MyList.Count)
 {
  if((MyList[i] == szCheckItem) || (MyList[i] == szInvertItem))
  {
   MyList.RemoveAt(i);
   continue;
  }
  i++;
 }

 checkID++;
}

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